unit vcmi.image_loaders;

{ This file is a part of Map editor for VCMI project.

  Copyright (C) 2016-2017 Alexander Shishkin alexvins@users.sourceforge.net

  This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
  License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
  version.

  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  details.

  A copy of the GNU General Public License is available on the World Wide Web at
  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}

{$I compilersetup.inc}

interface

uses
  Classes, SysUtils, fgl,
  FPimage,
  FPReadBMP,FPReadJPEG,FPReadPNG,FPReadTGA,FPReadTiff,FPReadPCX,

  Graphics, IntfGraphics, GraphType, LazLoggerBase,

  editor_classes, filesystem_base, vcmi.image_formats.h3pcx;

type

  { TImageResource }

  TImageResource = class(TBaseResource, IResource)
  private
    FData: TPicture;
  public
    constructor Create(APath: AnsiString);
    destructor Destroy; override;

    procedure LoadFromStream(AFileName: AnsiString; AStream: TStream); override;

    property Data: TPicture read FData;
  end;

  { TIntfImageResource }

  //RGBA intf bitmap

  TIntfImageResource = class(TBaseResource, IResource)
  private
    FData: TLazIntfImage;
    FRawImage: TRawImage;
  public
    constructor Create(APath: AnsiString);
    destructor Destroy; override;

    procedure LoadFromStream(AFileName: AnsiString; AStream: TStream); override;

    property Data: TLazIntfImage read FData;
  end;

  TReadersMap = specialize TFPGMap<AnsiString, TFPCustomImageReaderClass>;

  { TImageReaders }

  TImageReaders = class sealed
  private
    FUpdated: Boolean;
    FMap:TReadersMap;
  public
    constructor Create;
    destructor Destroy; override;

    function GetLoader(AExt: string): TFPCustomImageReaderClass;

    procedure CheckUpdated;

    class procedure Load(AFileName: AnsiString; ASource: TStream; ATarget: TLazIntfImage); static;
  end;

var
  GImageLoaders: TImageReaders;

implementation

uses
  stream_adapter, strutils;

function isH3PCX(AStream: TStream ): Boolean;
var
  initial_pos: Int64;
  width, height: UInt32;
  size: UInt64;
  source: TStreamReadAdapter;
begin
  initial_pos := AStream.Position;

  Result := false;

  source.Create(AStream);

  size := source.ReadDWord;
  width := source.ReadDWord;
  height := source.ReadDWord;

  AStream.Seek(initial_pos, soBeginning);

  if (size > 10000000) or (width > 100000) or (height > 100000) then
  begin
    exit;
  end;

  if size = width * height * 3 then
  begin
    Result := true;
  end
  else if size = width * height then
  begin
    Result := true;
  end;

end;

{ TImageReaders }

procedure TImageReaders.CheckUpdated;
var
  i: Integer;
  typename, ext, exts, s: String;
  c: TFPCustomImageReaderClass;
  exts_ar: TStringArray;
begin
  if not FUpdated then
  begin
    FMap.Clear;

    for i := 0 to ImageHandlers.Count - 1 do
    begin
      typename := ImageHandlers.TypeNames[i];
      c := ImageHandlers.ImageReader[typename];

      if Assigned(c) then
      begin
        exts := UpperCase(ImageHandlers.Extensions[typename]);
        exts_ar := exts.Split([';']);

        for s in exts_ar do
        begin
          ext := '.'+Trim(s);
          FMap.Add(ext,c);

          DebugLn('Registered image format loader %s(%s) for file type %s',[typename, c.ClassName, ext]);
        end;
      end;
    end;
  end;
end;

constructor TImageReaders.Create;
begin
  FMap := TReadersMap.Create;
end;

destructor TImageReaders.Destroy;
begin
  FMap.Free;
  inherited Destroy;
end;

function TImageReaders.GetLoader(AExt: string): TFPCustomImageReaderClass;
var
  idx: LongInt;
begin
  Result := nil;
  idx := FMap.IndexOf(AExt);
  if idx >= 0 then
  begin
    Result := FMap.Data[idx];
  end;
end;

class procedure TImageReaders.Load(AFileName: AnsiString; ASource: TStream; ATarget: TLazIntfImage);
var
  ext: AnsiString;
  Loader: TFPCustomImageReader;
  is_pcx: Boolean;
  LoaderType: TFPCustomImageReaderClass;
begin
  ext:=UpperCase(ExtractFileExt(AFileName));

  is_pcx := isH3PCX(ASource);

  if is_pcx then
  begin
    LoadH3Pcx(ASource, ATarget);
  end
  else
  begin
    LoaderType := GImageLoaders.GetLoader(ext);

    if Assigned(LoaderType) then
    begin
      Loader := LoaderType.Create();

      try
        ATarget.LoadFromStream(ASource, Loader)
      finally
        FreeAndNil(Loader);
      end;
    end
    else
    begin
      raise Exception.CreateFmt('Unknown image file extension for ',[AFileName]);
    end;
  end;
end;

{ TIntfImageResource }

constructor TIntfImageResource.Create(APath: AnsiString);
begin
  inherited Create(TResourceType.Image, APath);
  FData := TLazIntfImage.Create(0,0);

  FRawImage.Init;
  FRawImage.Description.Init_BPP32_R8G8B8A8_BIO_TTB(0,0);
  FRawImage.CreateData(false);

  FData.SetRawImage(FRawImage);
end;

destructor TIntfImageResource.Destroy;
begin
  FData.Free;
  inherited Destroy;
end;

procedure TIntfImageResource.LoadFromStream(AFileName: AnsiString; AStream: TStream);
begin
  TImageReaders.Load(AFileName, AStream, FData);
end;

{ TImageResource }

constructor TImageResource.Create(APath: AnsiString);
begin
  inherited Create(TResourceType.Image, APath);

  FData := TPicture.Create;
end;

destructor TImageResource.Destroy;
begin
  FData.Free;
  inherited Destroy;
end;

procedure TImageResource.LoadFromStream(AFileName: AnsiString; AStream: TStream);
var
  ATempImage: TLazIntfImage;
begin
  ATempImage := FData.Bitmap.CreateIntfImage;

  try
    TImageReaders.Load(AFileName, AStream, ATempImage);
    FData.Bitmap.LoadFromIntfImage(ATempImage);
  finally
    ATempImage.Free;
  end;
end;

initialization
  GImageLoaders := TImageReaders.Create;
finalization;
  FreeAndNil(GImageLoaders);
end.

